package com.xli.mybatis.plugin;

import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
import com.xli.mybatis.anno.UniqueCheck;
import com.xli.mybatis.exception.UniqueCheckException;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ResultMap;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.scripting.defaults.RawSqlSource;
import org.apache.ibatis.session.Configuration;

import java.lang.reflect.Field;
import java.util.*;

/**
 * 唯一约束插件
 */
@Slf4j
@Intercepts({@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})})
public class UniqueCheckPlugin implements InnerInterceptor {


    @Override
    public void beforeUpdate(Executor executor, MappedStatement ms, Object parameter) {
        // 获取 SQL 命令类型（INSERT、UPDATE）
        String sqlCommandType = ms.getSqlCommandType().name();

        if ("INSERT".equals(sqlCommandType) || "UPDATE".equals(sqlCommandType)) {
            Class<?> parameterClass;
            Object entity = null;

            // 处理参数对象
            if (parameter instanceof Map<?, ?> paramMap) {
                // 优先从 "et" 键获取实体类
                if (paramMap.containsKey("et")) {
                    entity = paramMap.get("et");
                }
                // 如果 "et" 不存在，尝试从 "param1" 获取
                else if (paramMap.containsKey("param1")) {
                    entity = paramMap.get("param1");
                }
            } else {
                // 如果不是 Map 类型，直接将参数视为实体类
                entity = parameter;
            }

            if (entity != null) {
                parameterClass = entity.getClass();
                for (Field field : parameterClass.getDeclaredFields()) {
                    if (field.isAnnotationPresent(UniqueCheck.class)) {
                        field.setAccessible(true);

                        try {
                            Object fieldValue = field.get(entity);
                            if (fieldValue != null) {
                                List<Long> idList = checkValueExists(field, fieldValue, parameterClass);
                                if (idList != null && !idList.isEmpty()) {
                                    Object value = null;
                                    for (Field primary : parameterClass.getDeclaredFields()) {
                                        if (primary.isAnnotationPresent(TableId.class)) {
                                            primary.setAccessible(true);
                                            value = primary.get(entity);
                                            break;
                                        }
                                    }
                                    if (value != null && !idList.contains((Long) value)) {
                                        throw new UniqueCheckException(field.getAnnotation(UniqueCheck.class).message());
                                    }
                                }
                            }
                        } catch (UniqueCheckException e) {
                            log.error(e.getMessage(), e);
                            throw new UniqueCheckException(e);
                        } catch (IllegalArgumentException | IllegalAccessException e) {
                            log.error(e.getMessage(), e);
                        }
                    }
                }
            }

        }
    }

    private List<Long> checkValueExists(Field field, Object value, Class<?> entityClass) {
        String tableName = getTableName(entityClass);
        String columnName = getColumnName(field);
        // 拼接 SQL 语句
        String checkSql = "SELECT ID FROM " + tableName + " WHERE " + columnName + " = #{value}";
        try (var sqlSession = SqlHelper.FACTORY.openSession()) {
            MappedStatement mappedStatement = createMappedStatement(sqlSession.getConfiguration(), checkSql, entityClass);
            Map<String, Object> params = new HashMap<>();
            params.put("value", value);
            return sqlSession.selectList(mappedStatement.getId(), params);
        }
    }

    private String getTableName(Class<?> entityClass) {
        if (entityClass.isAnnotationPresent(TableName.class)) {
            TableName tableNameAnnotation = entityClass.getAnnotation(TableName.class);
            return tableNameAnnotation.value();
        }
        return entityClass.getSimpleName();
    }

    private String getColumnName(Field field) {
        if (field.isAnnotationPresent(TableField.class)) {
            TableField tableFieldAnnotation = field.getAnnotation(TableField.class);
            return tableFieldAnnotation.value();
        }
        return field.getName();
    }

    private MappedStatement createMappedStatement(Configuration configuration, String sql, Class<?> resultType) {
        String statementId = "dynamic_sql_" + sql.hashCode();

        // 检查是否已经注册
        if (configuration.hasStatement(statementId)) {
            return configuration.getMappedStatement(statementId);
        }

        SqlSource sqlSource = new RawSqlSource(configuration, sql, Object.class);

        MappedStatement.Builder builder = new MappedStatement.Builder(configuration, statementId, sqlSource, SqlCommandType.SELECT);

        builder.resultMaps(Collections.singletonList(new ResultMap.Builder(configuration, "defaultResultMap", resultType, new ArrayList<>()).build()));

        MappedStatement mappedStatement = builder.build();
        configuration.addMappedStatement(mappedStatement);
        return mappedStatement;
    }
}
